#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ide.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/atomic.h>
#include <linux/poll.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "icm20608reg.h"


#define icm20608_NAME  "icm20608"
#define icm20608_CNT   1

struct icm20608_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *node;
    int minor;
    int major;
    int csgpio;
    void *private_data;
    signed int gyro_x_adc;
    signed int gyro_y_adc;
    signed int gyro_z_adc;
    signed int accel_x_adc;
    signed int accel_y_adc;
    signed int accel_z_adc;
    signed int temp_adc;
    struct regmap *regmap;
    struct regmap_config regmap_config;
};


static unsigned char icm20608_read_regs(struct icm20608_dev *dev, u8 reg)
{
    u8 ret;
    unsigned int data;

    ret = regmap_read(dev->regmap,reg,&data);
    return (u8)data;
}

static void icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 value)
{
    regmap_write(dev->regmap,reg,value);
}



void icm20608_readdata(struct icm20608_dev *dev)
{
    u8 ret;
    unsigned char data[14] = {0};

    ret = regmap_bulk_read(dev->regmap,ICM20_ACCEL_XOUT_H,data,14);

    dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
	dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
	dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
	dev->temp_adc    = (signed short)((data[6] << 8) | data[7]); 
	dev->gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); 
	dev->gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);
	dev->gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);
}

static int icm20608_open(struct inode *inode, struct file *filp)
{
    struct icm20608_dev *dev = container_of(inode->i_cdev,struct icm20608_dev,cdev);
    filp->private_data = dev;

    return 0;
}




static ssize_t icm20608_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
    signed int data[7];
    long err = 0;
    struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;

    icm20608_readdata(dev);
    data[0] = dev->gyro_x_adc;
	data[1] = dev->gyro_y_adc;
	data[2] = dev->gyro_z_adc;
	data[3] = dev->accel_x_adc;
	data[4] = dev->accel_y_adc;
	data[5] = dev->accel_z_adc;
	data[6] = dev->temp_adc;
	err = copy_to_user(buf, data, sizeof(data));

    return 0;
}

static int icm20608_release(struct inode *inode, struct file *filp)
{
    printk("user space exit\r\n");
    return 0;
}


static struct file_operations icm20608_operations = {
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};


void icm20608_reginit(struct icm20608_dev *dev)
{
    u8 value = 0;

   	icm20608_write_regs(dev, ICM20_PWR_MGMT_1, 0x80);
	mdelay(50);
	icm20608_write_regs(dev, ICM20_PWR_MGMT_1, 0x01);
	mdelay(50);

	value = icm20608_read_regs(dev, ICM20_WHO_AM_I);
	printk("ICM20608 ID = %#X\r\n", value);	

	icm20608_write_regs(dev, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/
	icm20608_write_regs(dev, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/
	icm20608_write_regs(dev, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/
	icm20608_write_regs(dev, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/
	icm20608_write_regs(dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
	icm20608_write_regs(dev, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/
	icm20608_write_regs(dev, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/
	icm20608_write_regs(dev, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO				*/	
}

static int icm20608_probe(struct spi_device *spi)
{
    int ret = 0;

    struct icm20608_dev *icm20608dev = devm_kzalloc(&spi->dev,sizeof(*icm20608dev),GFP_KERNEL);
    if(!icm20608dev){
        printk("分配内存失败\r\n");
        return -ENOMEM;
    }
    icm20608dev->regmap_config.reg_bits = 8; 
    icm20608dev->regmap_config.val_bits = 8;
    icm20608dev->regmap_config.read_flag_mask = 0x80;
    
    /* 初始化IIC接口的regmap */
	icm20608dev->regmap = regmap_init_spi(spi, &icm20608dev->regmap_config);
	if (IS_ERR(icm20608dev->regmap)) {
        printk("regmap_init failed!\r\n");
		return  PTR_ERR(icm20608dev->regmap);
	}	

    printk("the driver is compatible to the device!\r\n");

    if(icm20608dev->major){
        icm20608dev->devid = MKDEV(icm20608dev->major,0);
        ret = register_chrdev_region(icm20608dev->devid,0,icm20608_NAME);
        if(ret < 0){
            printk("request region\r\n");
            ret = -EINVAL;
            goto register_fail;
        }
    }
    else{
        ret = alloc_chrdev_region(&icm20608dev->devid,0,1,icm20608_NAME);
        if(ret < 0){
            printk("request region\r\n");
            ret = -EINVAL;
            goto register_fail;
        }
        icm20608dev->major = MAJOR(icm20608dev->devid);
        icm20608dev->minor = MINOR(icm20608dev->devid);
    }
    
    icm20608dev->cdev.owner = THIS_MODULE;
    cdev_init(&icm20608dev->cdev,&icm20608_operations);
    if(cdev_add(&icm20608dev->cdev,icm20608dev->devid,icm20608_CNT)){
        printk("cdev add failer\r\n");
        goto cdev_fail;
        ret = -EINVAL;
    }
    
    icm20608dev->class = class_create(THIS_MODULE,icm20608_NAME);
    if(IS_ERR(icm20608dev->class)){
        ret = PTR_ERR(icm20608dev->class);
        goto class_fail;
    }

    icm20608dev->device = device_create(icm20608dev->class,NULL,icm20608dev->devid,NULL,icm20608_NAME);
    if(IS_ERR(icm20608dev->device)){
        printk("device create failed\r\n");
        goto device_fail;
        ret = PTR_ERR(icm20608dev->device);
    }

    icm20608dev->private_data = spi;

    spi->mode = SPI_MODE_0;
    ret = spi_setup(spi);
    if (ret) {
     dev_err(&spi->dev, "Failed to setup SPI device: %d\n", ret);
     goto setup_fail;
    }

    icm20608_reginit(icm20608dev);
    spi_set_drvdata(spi,icm20608dev);

    return 0;

setup_fail:

device_fail:
    device_destroy(icm20608dev->class,icm20608dev->devid);

class_fail:
    class_destroy(icm20608dev->class);

cdev_fail:
    cdev_del(&icm20608dev->cdev);

register_fail:
    unregister_chrdev_region(icm20608dev->devid,icm20608_CNT);

regmap_exit(icm20608dev->regmap);

return ret;
}

static int icm20608_remove(struct spi_device *spi)
{
    struct icm20608_dev *icm20608dev = spi_get_drvdata(spi);

    gpio_free(icm20608dev->csgpio);
    device_destroy(icm20608dev->class,icm20608dev->devid);
    class_destroy(icm20608dev->class);

    cdev_del(&icm20608dev->cdev);
    unregister_chrdev_region(icm20608dev->devid,icm20608_CNT);

    printk("remove\r\n");

    return 0;
}

/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {
	{"gary,icm20608", 0},  
	{}
};

//device-tree
static const struct of_device_id icm20608_of_match[]={
    {.compatible = "gary,icm20608"},
    {}              
};

static struct spi_driver icm20608_driver = {
    .probe = icm20608_probe,
    .remove = icm20608_remove,
    .driver = {
        .owner = THIS_MODULE,
        .of_match_table = icm20608_of_match,
        .name = "icm20608",
    },
    .id_table = icm20608_id,
};

static int __init icm20608driver_init(void)
{
    int ret = 0;
    ret = spi_register_driver(&icm20608_driver);
    return ret;
}

static void __exit icm20608driver_exit(void)
{
    spi_unregister_driver(&icm20608_driver);
}


module_init(icm20608driver_init);
module_exit(icm20608driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GARY");